﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using scripts.logic;
using EventHandler = scripts.logic.EventHandler;

namespace scripts.net
{
	public static class NetManager
	{
		public const string SERVER_NAME = "[Polygon Battele Server]";
		//监听socket
		public static Socket listenfd;
		//客户端socket列表
		public static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();

		//Select检查列表
		private static List<Socket> checkRead = new List<Socket>();

		//Ping间隔
		public static long pingInterval = 30;

		public static void StartLoop(int listenPort)
		{
			listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

			IPAddress iPAddress = IPAddress.Parse("0.0.0.0");
			IPEndPoint ip = new IPEndPoint(iPAddress, listenPort);
			listenfd.Bind(ip);

			listenfd.Listen(0);
			Console.WriteLine(SERVER_NAME + "服务器已启动");

			while (true)
			{
				ResetCheckRead();
				Socket.Select(checkRead, null, null, 1000);
				for (int i = checkRead.Count - 1; i >= 0; --i)
				{
					Socket socket = checkRead[i];
					if (socket == listenfd)
					{
						ReadListenfd(socket);
					}
					else
					{
						ReadClientfd(socket);
					}
				}
				Timer();
			}
		}

		public static void ResetCheckRead()
		{
			//TODO　可否优化？
			checkRead.Clear();
			checkRead.Add(listenfd);
			foreach (ClientState s in clients.Values)
			{
				checkRead.Add(s.socket);
			}
		}

		public static void ReadListenfd(Socket listenfd)
		{
			try
			{
				Socket clientfd = listenfd.Accept();
				Console.WriteLine("Accept " + clientfd.RemoteEndPoint.ToString());
				ClientState clientState = new ClientState();
				clientState.socket = clientfd;
				clients.Add(clientfd, clientState);
			}
			catch (SocketException e)
			{
				Console.WriteLine("Accept Fail " + e.ToString());
			}
		}

		public static void ReadClientfd(Socket clientfd)
		{
			ClientState state = clients[clientfd];
			ByteArray readBuff = state.readBuff;
			//接收
			int cnt = 0;
			//当其缓冲区不够，清除；依旧不够，则返回
			//TODO 优化
			//缓冲区长度只有1024，单条协议超过此长度是会发送错误，需要调整长度
			if (readBuff.remain <= 0)
			{
				OnReceiveData(state);
				readBuff.MoveLowLength();
			}
			else
			{
				Console.WriteLine("Receive fail, maybe msg length > buff capacity");
				Close(state);
				return;
			}

			try
			{
				cnt = clientfd.Receive(readBuff.bytes, readBuff.writeIdx, readBuff.remain, 0);
			}
			catch (SocketException e)
			{
				Console.WriteLine("Socket SocketException " + e.ToString());
				Close(state);
				return;
			}

			//客户端关闭
			if(cnt <= 0)
			{
				Console.WriteLine("Socket Close " + clientfd.RemoteEndPoint.ToString());
				Close(state);
				return;
			}

			//消息处理
			readBuff.writeIdx += cnt;
			//处理二进制消息
			OnReceiveData(state);
			//移动缓冲区
			readBuff.MoveLowLength();
		}

		//定时器
		public static void Timer()
		{
			//消息分发
			MethodInfo mei = typeof(EventHandler).GetMethod("OnTimer");
			object[] ob = { };
			mei.Invoke(null, ob);
		}

		public static void Close(ClientState state)
		{
			//事件分发
			MethodInfo mei = typeof(EventHandler).GetMethod("OnDisconnect");
			object[] ob = { state };
			if (mei != null)
			{
				mei.Invoke(null, ob);
			}
			else
			{
				Console.WriteLine("Close fail " + state.socket.RemoteEndPoint.ToString());
			}
			//关闭
			state.socket.Close();
			clients.Remove(state.socket);
		}

		public static void OnReceiveData(ClientState state)
		{
			ByteArray readBuff = state.readBuff;
			int readIdx = readBuff.readIdx;
			byte[] bytes = readBuff.bytes;
			//消息长度
			if(readBuff.length < 2)
			{
				return; 
			}
			Int16 bodyLength = (Int16)((bytes[readIdx + 1] << 8) | bytes[readIdx]);
			//消息体
			if(readBuff.length < bodyLength)
			{
				return;
			}
			readBuff.readIdx += 2;
			//解析协议名
			int nameCnt = 0;
			string protoName = BaseMsg.ProtoNameDecode(readBuff.bytes, readBuff.readIdx, out nameCnt);
			if(protoName == "")
			{
				Console.WriteLine("OnReceiveData ProtoName Fail");
				Close(state);
			}
			readBuff.readIdx += nameCnt;
			//解析协议体
			int bodyCnt = bodyLength - nameCnt;
			BaseMsg baseMsg = BaseMsg.Decode(protoName, readBuff.bytes, readBuff.readIdx, bodyCnt);
			readBuff.readIdx += bodyCnt;
			readBuff.MoveLowLength();
			//分发消息
			MethodInfo mi = typeof(MsgHandler).GetMethod(protoName);
			object[] o = { state, baseMsg };
			Console.WriteLine("Receive " + protoName);
			if(mi != null)
			{
				mi.Invoke(null, o);
			}
			else
			{
				Console.WriteLine("OnReceiveData Invoke fail " + protoName);
			}
			//继续读取消息
			if(readBuff.length > 2)
			{
				OnReceiveData(state);
			}
		}

		public static void Send(ClientState state, BaseMsg msg)
		{
			//状态判断
			if(state == null)
			{
				return;
			}
			if (state.socket.Connected)
			{
				return;
			}
			//数据编码
			byte[] nameBytes = BaseMsg.ProtoNameEncode(msg);
			byte[] bodyBytes = BaseMsg.Encode(msg);
			int len = nameBytes.Length + bodyBytes.Length;
			byte[] sendBytes = new byte[2 + len];
			//组装长度
			sendBytes[0] = (byte)(len % 256);
			sendBytes[1] = (byte)(len / 256);
			//组装名字
			Array.Copy(nameBytes, 0, sendBytes, 2, nameBytes.Length);
			//组装消息体
			Array.Copy(bodyBytes, 0, sendBytes, 2 + nameBytes.Length, bodyBytes.Length);
			//为简化代码，不设置回调
			try
			{
				//TODO 设置回调会怎么样？
				state.socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, null, null);
			}
			catch (SocketException e)
			{
				Console.WriteLine("Socket BeginSend SocketException " + e.ToString());
			}
		}

		public static long GetTimeStamp()
		{
			TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
			return Convert.ToInt64(ts.TotalSeconds);
		}
	}
}
